/*
 *    Copyright (c) 2014 Xiang Xu <xuxiang@mail.bnu.edu.cn>
 *
 *    Permission is hereby granted, free of charge, to any person
 *    obtaining a copy of this software and associated documentation
 *    files (the "Software"), to deal in the Software without
 *    restriction, including without limitation the rights to use,
 *    copy, modify, merge, publish, distribute, sublicense, and/or sell
 *    copies of the Software, and to permit persons to whom the
 *    Software is furnished to do so, subject to the following
 *    conditions:
 *
 *    The above copyright notice and this permission notice shall be
 *    included in all copies or substantial portions of the Software.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *    OTHER DEALINGS IN THE SOFTWARE.
 */

#include "facade_feature.h"
#include "facade_grammar.h"
#include "scale_model.h"
#include "pclwraper.h"

#include "pcl/common/centroid.h"
#include <pcl/common/common.h>
#include <pcl/search/kdtree.h>
#include <pcl/visualization/pcl_plotter.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/octree/octree.h>
#include <boost/progress.hpp>

#include <fstream>
#include <sstream>
#include <limits>
#include <assert.h>


FacadeFeature::FacadeFeature(int width, int height, boost::shared_ptr<FacadeGrammar> facade_grammar)
{
    init(width, height, facade_grammar);
}

FacadeFeature::~FacadeFeature()
{
    destroy();
}

FacadeFeature::FacadeFeature(boost::shared_ptr<FacadeGrammar> facade_grammar)
{
    facade_grammar_ = facade_grammar;
    //	init(res, extMin, extRange);
}

void FacadeFeature::init(int width, int height, boost::shared_ptr<FacadeGrammar> facade_grammar)
{
    facade_grammar_ = facade_grammar;

    width_ = width;
    height_ = height;

    depth_ = FEATURE_NUMBER + 1 + facade_grammar_->get_terminal_symbol_size();

    grd_.resize(depth_);

    for(int i = 0; i < depth_; ++i)
    {
        grd_[i] = new double*[height_];

        for(int j = 0; j < height_; ++j)
        {
            grd_[i][j] = new double[width_];

            for(int k = 0; k < width_; ++k)
            {
                grd_[i][j][k] = 0.0;
            }
        }
    }
}

void FacadeFeature::LoadGridScale(const char* filename)
{
    ifstream ifs(filename);
    std::string dummy;
    ifs >> dummy >> extents_min_.x;
    ifs >> dummy >> extents_min_.y;
    ifs >> dummy >> extents_min_.z;
    ifs >> dummy >> extents_range_.x;
    ifs >> dummy >> extents_range_.y;
    ifs >> dummy >> extents_range_.z;
    ifs.close();
}


void FacadeFeature::NormalizeFeatures()
{
    std::vector<double> maxs(FEATURE_NUMBER, std::numeric_limits<double>::min());
    std::vector<double> mins(FEATURE_NUMBER, std::numeric_limits<double>::max());
    assert(maxs.size() == FEATURE_NUMBER);
    assert(mins.size() == FEATURE_NUMBER);

    // first loop, find maximal and minimal
    for(int i = 0; i < width_; ++i)
    {
        for(int j = 0; j < height_; ++j)
        {
            for(int feature = 0; feature < FEATURE_NUMBER; ++feature)
            {
                maxs[feature] = maxs[feature] < GetProperty(i, j, feature) ?
                                GetProperty(i, j, feature) : maxs[feature];

                mins[feature] = mins[feature] > GetProperty(i, j, feature) ?
                                GetProperty(i, j, feature) : mins[feature];
            }
        }
    }

    for(int feature = 0; feature < FEATURE_NUMBER; ++feature)
    {
        std::cout << "\t" << "feature " << feature << " max = " << maxs[feature] <<
                  "\tmin = " << mins[feature] << std::endl;
    }

#if 1

    // second loop, normalize data to [-1, 1]
    for(int i = 0; i < width_; ++i)
    {
        for(int j = 0; j < height_; ++j)
        {

            for(int feature = 0; feature < FEATURE_NUMBER; ++feature)
            {
                double normalized_value = ScaleModel::AB2MN(GetProperty(i, j, feature),
                                          mins[feature], maxs[feature], -1, 1);

                SetProperty(i, j, feature, normalized_value);
            }
        }
    }

#endif
}

void FacadeFeature::CreateGridFromPointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr facade_cloud, double resolution)
{
    resolution_ = resolution;
    // building facade min/max extent
    pcl::PointXYZ min_extent, max_extent;
    pcl::getMinMax3D(*facade_cloud, min_extent, max_extent);

    width_ = (int)(extents_range_.x / resolution_ + 0.5);
    height_ = (int)(extents_range_.y / resolution_ + 0.5);
    convert_ratio_ = cv::Point2d((width_ - 1.0) / extents_range_.x, (height_ - 1.0) / extents_range_.y);

    depth_ = FEATURE_NUMBER + 1 + facade_grammar_->get_terminal_symbol_size();

    grd_.resize(depth_);

    for(int i = 0; i < depth_; ++i)
    {
        grd_[i] = new double*[height_];

        for(int j = 0; j < height_; ++j)
        {
            grd_[i][j] = new double[width_];

            for(int k = 0; k < width_; ++k)
            {
                grd_[i][j][k] = 0.0;
            }
        }
    }

    std::cout << "Compute normals...\n";
    pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
    PclWraper::ComputeNormal(facade_cloud, cloud_normals);
    std::cout << "Compute normals Done.\n";

    double votex_size = resolution_;// * 2;
    pcl::octree::OctreePointCloudSearch<pcl::PointXYZ>::Ptr octree(
        new pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> (votex_size));
    octree->defineBoundingBox(extents_min_.x, extents_min_.y, min_extent.z, extents_min_.x + extents_range_.x,
                              extents_min_.y + extents_range_.y, max_extent.z);
    octree->setInputCloud(facade_cloud);
    octree->addPointsFromInputCloud();

    boost::progress_display display(width_ * height_);

    Eigen::Vector4f max_pt, min_pt;
    pcl::PointXYZ search_point;

    for(int i = 0; i < width_; ++i)
    {
        for(int j = 0; j < height_; ++j)
        {
            // define x, y coordinate for search point
            search_point.x = i * resolution_ + extents_min_.x + resolution_ / 2.0;
            search_point.y = j * resolution_ + extents_min_.y + resolution_ / 2.0;

            // criteria
            int point_number = 0;
            double z_depth = 0.0;
            double curvature = 0.0;
            int not_empty_voxels = 0;

            // point-search along z-direction...
            for(double z = min_extent.z; z <= max_extent.z; z += resolution_)
            {
                // define z-coordinate for search point
                search_point.z = z;

                // search in the grid
                std::vector<int> indices;

                if(!octree->voxelSearch(search_point, indices))
                {
                    continue;
                }

                // get #points
                point_number += indices.size();

                // z-depth
                for(int n = 0; n < indices.size(); ++n)
                {
                    z_depth += facade_cloud->points[indices[n]].z;
                    curvature += cloud_normals->points[indices[n]].curvature;
                }
            }

            if(point_number == 0)
            {
                // cout << "indices.size() == " << indices.size() << endl;
                SetProperty(i, j, DENSITY, 0);
                SetProperty(i, j, DEPTH, min_extent.z);
                SetProperty(i, j, CURVATE, -0.1);
            }
            else
            {
                SetProperty(i, j, DENSITY, point_number);
                SetProperty(i, j, DEPTH, z_depth / point_number);
                SetProperty(i, j, CURVATE, curvature / point_number);
            }

            ++display;
        }
    }
}

void FacadeFeature::destroy()
{
    for(int i = 0; i < depth_; ++i)
    {
        if(grd_[i] != NULL)
        {
            for(int j = 0; j < height_; ++j)
            {
                delete[] grd_[i][j];
                grd_[i][j] = NULL;

            }

            delete[] grd_[i];
        }
    }

    resolution_ = 0;
    width_ = 0;
    height_ = 0;
    depth_ = 0;
}

double FacadeFeature::GetProperty(int width, int height, int depth)
{
    return grd_[depth][height][width];
}

double FacadeFeature::GetProperty(const cv::Point2d& pos, int depth)
{
    cv::Point2d localPos = pos - cv::Point2d(extents_min_.x, extents_min_.y);
    int width = (int)(localPos.x * convert_ratio_.x);
    int height = (int)(localPos.y * convert_ratio_.y);

    return grd_[depth][height][width];
}

void FacadeFeature::SetProperty(int width, int height, int depth, double value)
{
    // 	uchar* row = grd_[depth]->ptr<uchar>(height);
    // 	row[width] = (uchar)value;
    grd_[depth][height][width] = value;
}

void FacadeFeature::RenderGrds(std::string& path)
{
    cv::Mat label_mat(height_, width_, CV_8UC3);
    cv::Mat_<cv::Vec3b> _Mat = label_mat;

    for(int i = 0; i < height_; ++i)
    {
        for(int j = 0; j < width_; ++j)
        {
            double label = grd_[FEATURE_NUMBER][i][j];
            std::string symbol = facade_grammar_->get_terminal_symbol(label);
            std::vector<double> color = facade_grammar_->get_symbol_color(symbol);
            _Mat(height_ - 1 - i, j) [0] = (uchar)(int (color[0] * 255));
            _Mat(height_ - 1 - i, j) [1] = (uchar)(int (color[1] * 255));
            _Mat(height_ - 1 - i, j) [2] = (uchar)(int (color[2] * 255));
        }
    }

    imwrite((path + "label_color.png"), label_mat);

    for(size_t depth = 0; depth < grd_.size(); ++depth)
    {
        cv::Mat mat(height_, width_, CV_8UC1);

        // pass 1: find out max and min
        double max = std::numeric_limits<double>::min();
        double min = std::numeric_limits<double>::max();

        for(int i = 0; i < height_; ++i)
        {
            for(int j = 0; j < width_; ++j)
            {
                double value = grd_[depth][i][j];

                if(max < value)
                    max = value;

                if(min > value)
                    min = value;
            }
        }

        double range = max - min;

        if(range == 0)
            continue;

        // pass 2: convert value to 0-255 scale
        for(int i = 0; i < height_; ++i)
        {
			  uchar* row = mat.ptr<uchar> (height_ - 1 - i);
            for(int j = 0; j < width_; ++j)
            {
                double scaled_value = (grd_[depth][i][j] - min) / range;
                row[j] = (uchar)(scaled_value * 255);
            }
        }

        std::ostringstream image_path;

        if(depth == DENSITY)
            image_path << path << "density.png";
        else if(depth == DEPTH)
            image_path << path << "depth.png";
        else if(depth == CURVATE)
            image_path << path << "curvate.png";
        else if(depth == FEATURE_NUMBER)
            image_path << path << "label.png";
        else
            image_path << path <<
                       facade_grammar_->get_terminal_symbol(depth - FEATURE_NUMBER - 1) << ".png";

        imwrite(image_path.str(), mat);
    }
}


void FacadeFeature::SaveGrids(std::string& filename)
{
    // Save grid header.
    std::string grid_header = filename + "header.txt";
    std::ofstream ofs(grid_header.c_str());
    ofs << "Width " << width_ << std::endl;
    ofs << "Height " << height_ << std::endl;
    ofs << "Depth " << depth_ << std::endl;
    ofs << "Resolution " << resolution_ << std::endl;
    ofs << "Extent_Min " << extents_min_.x << " " << extents_min_.y << std::endl;
    ofs << "Extent_Range " << extents_range_.x << " " << extents_range_.y << std::endl;
    ofs << "Convert_Ratio " << convert_ratio_.x << " " << convert_ratio_.y << std::endl;
    ofs.close();

    // Save grids
    for(size_t depth = 0; depth < grd_.size(); ++depth)
    {
        std::ostringstream oss;

        if(depth == DENSITY)
            oss << filename << "density.txt";
        else if(depth == DEPTH)
            oss << filename << "depth.txt";
        else if(depth == CURVATE)
            oss << filename << "curvate.txt";
        else if(depth == FEATURE_NUMBER)
            oss << filename << "label.txt";
        else
            oss << filename <<
                facade_grammar_->get_terminal_symbol(depth - FEATURE_NUMBER - 1) << ".txt";

        std::ofstream ofs(oss.str().c_str());

        for(int i = 0; i < height_; ++i)
        {
            for(int j = 0; j < width_; ++j)
            {
                ofs << grd_[depth][i][j] << " ";
            }

            ofs << std::endl;
        }

        ofs.close();
    }
}

void FacadeFeature::LoadGrids(std::string& filename)
{
    // load header first
    std::string header_path = filename + "header.txt";
    std::ifstream ifs(header_path.c_str());
    std::string dummy;
    ifs >> dummy >> width_;
    ifs >> dummy >> height_;
    ifs >> dummy >> depth_;
    ifs >> dummy >> resolution_;
    ifs >> dummy >> extents_min_.x >> extents_min_.y;
    ifs >> dummy >> extents_range_.x >> extents_range_.y;
    ifs >> dummy >> convert_ratio_.x >> convert_ratio_.y;
    ifs.close();

    grd_.resize(depth_);

    for(size_t depth = 0; depth < depth_; ++depth)
    {
        std::ostringstream oss;

        if(depth == DENSITY)
            oss << filename << "density.txt";
        else if(depth == DEPTH)
            oss << filename << "depth.txt";
        else if(depth == CURVATE)
            oss << filename << "curvate.txt";
        else if(depth == FEATURE_NUMBER)
            oss << filename << "label.txt";
        else
            oss << filename <<
                facade_grammar_->get_terminal_symbol(depth - FEATURE_NUMBER - 1) << ".txt";

        std::ifstream ifs(oss.str().c_str());
        //	std::string new_path = filename + facade_grammar_->get_terminal_symbol(depth) + ".txt";
        //	std::ifstream ifs(new_path.c_str() );

        grd_[depth] = new double*[height_];

        for(int j = 0; j < height_; ++j)
        {
            grd_[depth][j] = new double[width_];

            for(int k = 0; k < width_; ++k)
            {
                grd_[depth][j][k] = 0.0;
            }
        }

        for(int i = 0; i < height_; ++i)
        {
            for(int j = 0; j < width_; ++j)
            {
                ifs >> grd_[depth][i][j];
            }
        }

        ifs.close();
    }
}

void FacadeFeature::SetSymbol(double xmin, double xmax, double ymin, double ymax, int symbol)
{
    for(int x = xmin; x < xmax; ++x)
    {
        for(int y = ymin; y < ymax; ++y)
        {
            SetProperty(x, y, FEATURE_NUMBER, symbol);
        }
    }
}

std::vector<double> FacadeFeature::Smooth(std::vector<double> feature, int left, int right)
{
    std::vector<double> new_feature;
    new_feature.resize(feature.size());

    for(int i = 0; i < feature.size(); ++i)
    {
        int count = 1;
        double sum = feature[i];

        for(int l = 1; l <= left; ++l)
        {
            if(i - l >= 0)
            {
                sum += feature[i - l];
                ++count;
            }
        }

        for(int r = 1; r <= right; ++r)
        {
            if(i + r < feature.size())
            {
                sum += feature[i + r];
                ++count;
            }
        }

        new_feature[i] = sum / count;
    }

    return new_feature;
}

std::vector<int> FacadeFeature::FindLoacalMinimalAndMaximal(std::vector<double>& feature)
{
    std::vector<int> mins_maxs;

    for(int i = 1; i < feature.size() - 1; ++i)
    {
        if(feature[i] < feature[i - 1] && feature[i] < feature[i + 1])
        {
            mins_maxs.push_back(i);
        }

        if(feature[i] > feature[i - 1] && feature[i] > feature[i + 1])
        {
            mins_maxs.push_back(i);
        }
    }

    return mins_maxs;
}

void FacadeFeature::CalculateParameters(std::string path)
{
    // smooth times
    const int SMOOTH_ITERATORS = 5;

    // features
    int symbols = this->facade_grammar_->get_terminal_symbol_size();
    vertical_features.resize(symbols);
    horizonal_features.resize(symbols);

    // actions
    action_parameters_horizontal_.resize(symbols);
    action_parameters_vertical_.resize(symbols);

    std::vector<double> width, height;

    for(int i = 0; i < width_; ++i)
    {
        width.push_back(i);
    }

    for(int j = 0; j < height_; ++j)
    {
        height.push_back(j);
    }

    // calculate features
    for(int s = 0; s < symbols; ++s)
    {
        // vertical feature
        for(int i = 0; i < height_; ++i)
        {
            double sum = 0;

            for(int j = 0; j < width_; ++j)
            {
                sum += grd_[s + FEATURE_NUMBER + 1][i][j];
            }

            sum /= width_;
            vertical_features[s].push_back(sum);
        }

        // horizontal feature
        for(int i = 0; i < width_; ++i)
        {
            double sum = 0;

            for(int j = 0; j < height_; ++j)
            {
                sum += grd_[s + FEATURE_NUMBER + 1][j][i];
            }

            sum /= height_;
            horizonal_features[s].push_back(sum);
        }

        // smooth feature
        for(int i = 0; i < SMOOTH_ITERATORS; ++i)
        {
            // 5 neighbers average ...
            vertical_features[s] = Smooth(vertical_features[s], 5, 5);
            horizonal_features[s] = Smooth(horizonal_features[s], 5, 5);
        }

        // feature difference ...
        for(int i = 0; i < height_ - 1; ++i)
        {
            vertical_features[s][i] = vertical_features[s][i + 1] - vertical_features[s][i];
        }

        vertical_features[s][height_ - 1] = 0.0;

        for(int i = 0; i < width_ - 1; ++i)
        {
            horizonal_features[s][i] = horizonal_features[s][i + 1] - horizonal_features[s][i];
        }

        horizonal_features[s][width_ - 1] = 0.0;


        // Render the features on the probability image
        cv::Mat mat(height_, width_, CV_8UC3);
        cv::Mat mat2(height_, width_, CV_8UC3);

        // pass 1: find out max and min
        double max = std::numeric_limits<double>::min();
        double min = std::numeric_limits<double>::max();

        for(int i = 0; i < height_; ++i)
        {
            for(int j = 0; j < width_; ++j)
            {
                double value = grd_[s + FEATURE_NUMBER + 1][i][j];

                if(max < value)
                    max = value;

                if(min > value)
                    min = value;
            }
        }

        double range = max - min;

        if(range == 0)
            continue;

        cv::Mat_<cv::Vec3b> _Mat = mat;
        cv::Mat_<cv::Vec3b> _Mat2 = mat2;

        // pass 2: convert value to 0-255 scale
        for(int i = 0; i < height_; ++i)
        {
            for(int j = 0; j < width_; ++j)
            {
                //uchar* row = mat.ptr<uchar>(i);
                // flip!!!
                double scaled_value = (grd_[s + FEATURE_NUMBER + 1][height_ - 1 - i][j] - min) / range;
                // row[j] = (uchar)(scaled_value * 255);
                _Mat(i, j) [0] = (uchar)(scaled_value * 255);
                _Mat(i, j) [1] = (uchar)(scaled_value * 255);
                _Mat(i, j) [2] = (uchar)(scaled_value * 255);
                _Mat2(i, j) [0] = (uchar)(scaled_value * 255);
                _Mat2(i, j) [1] = (uchar)(scaled_value * 255);
                _Mat2(i, j) [2] = (uchar)(scaled_value * 255);
            }
        }

        // first loop find out the max & min
        double max_vertical = std::numeric_limits<double>::min();
        double min_vertical = std::numeric_limits<double>::max();
        double max_horizonal = std::numeric_limits<double>::min();
        double min_horizonal = std::numeric_limits<double>::max();

        for(int i = 0; i < vertical_features[s].size(); ++i)
        {
            max_vertical = max_vertical < vertical_features[s][i] ? vertical_features[s][i] : max_vertical;
            min_vertical = min_vertical > vertical_features[s][i] ? vertical_features[s][i] : min_vertical;
        }

        for(int i = 0; i < horizonal_features[s].size(); ++i)
        {
            max_horizonal = max_horizonal < horizonal_features[s][i] ? horizonal_features[s][i] : max_horizonal;
            min_horizonal = min_horizonal > horizonal_features[s][i] ? horizonal_features[s][i] : min_horizonal;
        }

        // second loop
        cv::Point** points = new cv::Point*[2];
        points[0] = new cv::Point[vertical_features[s].size()];
        points[1] = new cv::Point[horizonal_features[s].size()];

        for(int i = 0; i < vertical_features[s].size(); ++i)
        {
            double value = (width_ * (vertical_features[s][i] - min_vertical) / (max_vertical - min_vertical));
            points[0][i] = cv::Point(value, height_ - 1 - i);
        }

        for(int i = 0; i < horizonal_features[s].size(); ++i)
        {
            double value = (height_ * (horizonal_features[s][i] - min_horizonal) / (max_horizonal - min_horizonal));
            points[1][i] = cv::Point(i, height_ - 1 - value);
        }

        int thickness = 2;
        int line_type = 2;

        int* npts = new int[2];
        npts[0] = height_;
        npts[1] = width_;

        cv::polylines(mat, (const cv::Point**) &points[0], (const int*) &npts[0], 1, false, cv::Scalar(255, 0, 0), thickness, line_type);

        cv::polylines(mat2, (const cv::Point**) &points[1], (const int*) &npts[1], 1, false, cv::Scalar(0, 255, 0), thickness, line_type);


        // Find out the maxinal and mininal point of the feature polyline
        // And get the action parameter of grammar rule
        std::vector<int> vertical_lines, horizontal_lines;

        vertical_lines = FindLoacalMinimalAndMaximal(vertical_features[s]);
        horizontal_lines = FindLoacalMinimalAndMaximal(horizonal_features[s]);

        if(vertical_features[s][vertical_lines[0]] < vertical_features[s][0])
        {
            action_parameters_vertical_[s].push_back(vertical_lines[0]);
            cv::line(mat, cv::Point(width_ / 2, height_ - 1), cv::Point(width_ / 2, height_ - 1 - vertical_lines[0]),
                     cv::Scalar(0, 0, 255), line_type);
        }

        for(int i = 1; i < vertical_lines.size(); ++i)
        {
            if(vertical_features[s][vertical_lines[i]] < vertical_features[s][vertical_lines[i - 1]])
            {
                action_parameters_vertical_[s].push_back(vertical_lines[i] - vertical_lines[i - 1]);

                cv::line(mat, cv::Point(width_ / 2, height_ - 1 - vertical_lines[i - 1]), cv::Point(width_ / 2, height_ - 1 - vertical_lines[i]),
                         cv::Scalar(0, 0, 255), line_type);
            }
        }

        if(vertical_features[s][height_ - 1] < vertical_features[s][vertical_lines[vertical_lines.size() - 1]])
        {
            cv::line(mat, cv::Point(width_ / 2, height_ - 1 - height_), cv::Point(width_ / 2, height_ - 1 - vertical_lines[vertical_lines.size() - 1]),
                     cv::Scalar(0, 0, 255), line_type);
        }

        if(horizonal_features[s][horizontal_lines[0]] < horizonal_features[s][0])
        {
            action_parameters_horizontal_[s].push_back(horizontal_lines[0]);
            cv::line(mat2, cv::Point(0, height_ - 1 - height_ / 2), cv::Point(horizontal_lines[0], height_ - 1 - height_ / 2),
                     cv::Scalar(0, 0, 255), line_type);
        }

        for(int i = 1; i < horizontal_lines.size(); ++i)
        {
            if(horizonal_features[s][horizontal_lines[i]] < horizonal_features[s][horizontal_lines[i - 1]])
            {
                action_parameters_horizontal_[s].push_back(horizontal_lines[i] - horizontal_lines[i - 1]);

                cv::line(mat2, cv::Point(horizontal_lines[i - 1], height_ - 1 - height_ / 2), cv::Point(horizontal_lines[i], height_ - 1 - height_ / 2),
                         cv::Scalar(0, 0, 255), line_type);
            }
        }

        if(horizonal_features[s][width_ - 1] < horizonal_features[s][horizontal_lines[horizontal_lines.size() - 1]])
        {
            action_parameters_horizontal_[s].push_back(width_ - horizontal_lines[horizontal_lines.size() - 1]);
            cv::line(mat2, cv::Point(horizontal_lines[horizontal_lines.size() - 1], height_ - 1 - height_ / 2), cv::Point(width_, height_ - 1 - height_ / 2),
                     cv::Scalar(0, 0, 255), line_type);
        }

        std::ostringstream image_path1, image_path2;
        image_path1 << path << facade_grammar_->get_terminal_symbol(s) << "_vertial.png";
        image_path2 << path << facade_grammar_->get_terminal_symbol(s) << "_horizontal.png";
        imwrite(image_path1.str(), mat);
        imwrite(image_path2.str(), mat2);

        delete[] npts;
        delete[] points[0];
        delete[] points[1];
        delete[] points;
    }
}

void FacadeFeature::SaveParameters(std::string parater_path)
{
    std::ofstream fout(parater_path.c_str());
    const int extend = 10;
    const int step = 2;

    fout << "vertical_parameters " << " " << action_parameters_vertical_.size() << std::endl;

    for(int i = 0; i < action_parameters_vertical_.size(); ++i)
    {
        fout << "symbol-" << facade_grammar_->get_terminal_symbol(i)
             << "\t" << action_parameters_vertical_[i].size() << std::endl;

        for(int j = 0; j < action_parameters_vertical_[i].size(); ++j)
        {
            //for (int k = action_parameters_vertical_[i][j] / 2; k <= action_parameters_vertical_[i][j]; k += step)
            //fout << k << "\t";
            fout << action_parameters_vertical_[i][j] << "\t";
        }

        fout << endl;
    }

    fout << std::endl;

    fout << "horizontal_parameters " << " " << action_parameters_horizontal_.size() << std::endl;

    for(int i = 0; i < action_parameters_horizontal_.size(); ++i)
    {
        fout << "symbol-"  << facade_grammar_->get_terminal_symbol(i)
             << "\t" << action_parameters_horizontal_[i].size() << endl;

        for(int j = 0; j < action_parameters_horizontal_[i].size(); ++j)
        {
            //for (int k = action_parameters_horizontal_[i][j] / 2; k <= action_parameters_horizontal_[i][j]; k += step)
            //	fout << k << "\t";
            fout << action_parameters_horizontal_[i][j] << "\t";
        }

        fout << endl;
    }

    fout.close();
}
